<?PHP
/*
 * $Id: barge-mecab.classes.php 139 2009-12-16 15:29:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\mecab;


use barge\System;
use barge\logging\LogFactory;
use barge\utils\ArrayList;
use barge\utils\Bean;
use barge\utils\NestedException;
use barge\utils\String;

/**
 * Mecab 例外
 * @see NestedException
 */
class MecabException extends NestedException
{
}

/**
 * 形態素解析エンジン MeCab
 * @since 1.3 2009/06/20
 * @version 1.0 2009/06/20
 */
abstract class Mecab
{
	/**
	 * @return Mecab
	 */
	public static function getInstance()
	{
		if (System::existsExtension('mecab')) {
			return new MecabExtension();	// php_mecab.dll
		} else {
			return new MecabProcess();		// mecab.exe
		}
	}

	/**
	 * @param string $text
	 * @return ArrayList
	 */
	abstract public function analyze($text);
}

/**
 * php_mecab を使用する
 * @since 1.3 2009/06/20
 * @version 1.0 2009/06/20
 * @see Mecab
 */
class MecabExtension extends Mecab
{
	/**
	 * @param string $text
	 * @return ArrayList
	 * @see Mecab#analyze
	 */
	public function analyze($text)
	{
		$mecab = mecab_new();
		$list = new ArrayList();
		if ($node = mecab_sparse_tonode($mecab, $text)) {
			while ($node) {
				$fields = mecab_node_toarray($node);
				$list->add(new MecabNode($fields["surface"], $fields['char_type'], null, 0, 0, new Features($fields['feature'])));
				$node = mecab_node_next($node);
			}
		}
		mecab_destroy($mecab);
		return $list;
	}
}

/**
 * 形態素解析エンジン MeCab
 *
 * @since 1.3 2009/06/20
 * @version 1.0 2009/06/20
 */
class MecabProcess extends Mecab
{
	/**
	 * @param string $text
	 * @return ArrayList
	 */
	public function analyze($text)
	{
		if (String::isBlank($text)) {
			return new ArrayList();
		}

		static $arg = ' --node-format=%m\\t%t\\t%h\\t%ps\\t%pe\\t%F\\t[0,1,2]\\n --eos-format=';
		$exepath = System::firstNotEmpty(System::getProperty('mecab/path'), 'mecab');

		$shell = System::getShell();
		$out = $shell->exec(String::quote($exepath, '"').$arg, $text);
		$lines = explode("\n", $out);

		$list = new ArrayList();
		foreach ($lines as $line) {
			if (trim($line) === '') {
				continue;
			}
			$fields = explode("\t", trim($line));
			$list->add(new MecabNode($fields[0], $fields[1], $fields[2], $fields[3], $fields[4], new Features(array_slice($fields, 5))));
		}
		return $list;
	}
}

/**
 * 形態素解析ノード
 *
 * @since 1.3 2009/06/20
 * @version 1.0 2009/06/20
 */
class MecabNode
{
	const KANJI = 2;
	const SIGN = 3;
	const NUMBER = 4;
	const HIRAGANA = 6;
	const KATAKANA = 7;

	/* @var string */
	private $surface;

	/* @var int */
	private $characterType;

	/* @var Features */
	private $features;

	/* @var int */
	private $hinshiId;

	/* @var int */
	private $startPosition;

	/* @var int */
	private $endPosition;

	/**
	 *
	 * @param string $surface
	 * @param int $characterType
	 * @param int $hinshiId
	 * @param int $startPosition
	 * @param int $endPosition
	 * @param ArrayList $features
	 */
	public function __construct($surface, $characterType, $hinshiId, $startPosition, $endPosition, Features $features)
	{
		$this->surface = $surface;
		$this->characterType = $characterType;
		$this->features = $features;
		$this->hinshiId = $hinshiId;
		$this->startPosition = $startPosition;
		$this->endPosition = $endPosition;
	}

	/**
	 * @return string
	 */
	public function getSurface()
	{
		return $this->surface;
	}

	/**
	 * @return int
	 */
	public function getCharacterType()
	{
		return $this->characterType;
	}

	/**
	 * @return Features
	 */
	public function getFeatures()
	{
		return $this->features;
	}

	/**
	 * @return int
	 */
	public function getHinshiId()
	{
		return $this->hinshiId;
	}

	/**
	 * @return int
	 */
	public function getStartPosition()
	{
		return $this->startPosition;
	}

	/**
	 * @return int
	 */
	public function getEndPosition()
	{
		return $this->endPosition;
	}


}

/**
 * @since 1.3 2009/06/20
 * @version 1.0 2009/06/20
 */
class Features
{
	/** 名詞 */
	const NOUN = 1;
	/** 副詞 */
	const ADVERB = 2;
	/** 動詞 */
	const VERB = 3;
	/** 助動詞 */
	const HELPING_VERB = 4;
	/** 固有名詞 */
	const PROPER_NOUN = 5;
	/** 一般 */
	const COMMON = 6;
	/** 組織 */
	const ORGAN = 7;
	/** 記号 */
	const SYMBOL = 8;
	/** 読点 */
	const PAUSE_MARK = 9;

	/** @var array */
	private static $map = array(
		self::NOUN => '名詞',
		self::PROPER_NOUN => '固有名詞',
		self::VERB => '動詞',
		self::COMMON => '一般',
		self::ORGAN => '組織',
//		'自立',
//		'副詞可能',
//		'連体化',
//		'係助詞',
//		'格助詞',
//		'接尾',
//		'サ変接続',
//		'形容動詞語幹',
//		'感動詞',
		self::SYMBOL => '記号',
		self::PAUSE_MARK => '読点'
		);

	/**
	 * @var array
	 */
	private $list;

	/**
	 * @param string $list
	 */
	public function __construct(array $list)
	{
		$this->list = $list;
	}

	/**
	 * @param int $type
	 * @return bool
	 */
	public function contains($type)
	{
		return (array_search(self::$map[$type], $this->list, true) !== false);
	}
}

